#include <assert.h>
#include <imbus.h>
#include <stdio.h>
#include <test_object.h>

static const IMObjectClass *__point_parent_class = 0;
static const IMObjectClass *__circle_parent_class = 0;
static IMType __circle_type = 0;
static IMType __point_type = 0;

static void
__point_ctor (IMPointer point)
{
    assert (IS_POINT(point));

    ((Point *)point)->x = 0;
    ((Point *)point)->y = 0;
}

static void
__point_dtor (IMPointer point)
{
    assert (IS_POINT(point));
}

static void
__point_copy (IMPointer dest, IMConstPointer src)
{
    Point *dp = POINT (dest);
    const Point *sp = CONST_POINT (src);

    assert (dp && sp);

    __point_parent_class->copy (dest, src);

    dp->x = sp->x;
    dp->y = sp->y;

}

static IMBool
__point_serialize (IMConstPointer point, IMPointer stream)
{
    const Point *p  = CONST_POINT (point);
    IMStream *sp = IM_STREAM (stream);

    assert (p && sp);

    return __point_parent_class->serialize (point, stream) &&
           im_stream_put_int32 (stream, p->x) &&
           im_stream_put_int32 (stream, p->y);
}

static IMBool
__point_deserialize (IMPointer point, IMPointer stream)
{
    IMInt32  x;
    IMInt32  y;

    Point    *p  = POINT (point);
    IMStream *sp = IM_STREAM (stream);

    assert (p && sp);

    if (!__point_parent_class->deserialize (point, stream))
        return FALSE;

    if (im_stream_get_data_type (stream) != IM_TYPE_INT32 || !im_stream_get_int32 (stream, &x))
        return FALSE;

    if (im_stream_get_data_type (stream) != IM_TYPE_INT32 || !im_stream_get_int32 (stream, &y))
        return FALSE;

    p->x = x;
    p->y = y;

    return TRUE;
}
static void
__point_move_to (Point *point, int x, int y)
{
    assert (IS_POINT(point));

    point->x = x;
    point->y = y;
}

static void
__point_draw (Point *point)
{
    assert (IS_POINT(point));

    printf ("-Point object %p: x = %d, y = %d\n",
             (void*)point,
             point->x,
             point->y);
}

static void
__point_class_init (IMPointer klass)
{
    ((IMObjectClass *)klass)->copy  = __point_copy;
    ((IMObjectClass *)klass)->serialize = __point_serialize;
    ((IMObjectClass *)klass)->deserialize = __point_deserialize;

    ((PointClass *)klass)->move_to  = __point_move_to;
    ((PointClass *)klass)->draw     = __point_draw;

    __point_parent_class            = im_object_class_get_parent (klass);
}

IMType
point_get_type ()
{
    static IMTypeInfo point_info =
    {
        sizeof (PointClass),
        __point_class_init,
        0,

        sizeof (Point),
        __point_ctor,
        __point_dtor
    };

    if (!__point_type) {
        __point_type = im_type_register_class (IM_TYPE_OBJECT,
                                               "Point",
                                               &point_info,
                                               TRUE);
    }

    return __point_type;
}

Point*
point_new (int x, int y)
{
    Point *p = (Point *) im_object_new (TYPE_POINT);

    assert (p != 0);

    p->x = x;
    p->y = y;

    return p;
}

void
point_move_to (Point *point, int x, int y)
{
    const PointClass *cp = POINT_GET_CLASS (point);

    assert (cp != 0);

    if (cp->move_to)
        cp->move_to (point, x, y);
}

void
point_draw (Point *point)
{
    const PointClass *cp = POINT_GET_CLASS (point);

    assert (cp != 0);

    if (cp->draw)
        cp->draw (point);
}

int
point_get_x (Point *point)
{
    assert (IS_POINT (point));

    return point->x;
}

int
point_get_y (Point *point)
{
    assert (IS_POINT (point));

    return point->y;
}

static void
__circle_ctor (IMPointer circle)
{
    assert (IS_CIRCLE (circle));

    ((Circle*)circle)->radius = 0;
}

static void
__circle_dtor (IMPointer circle)
{
    assert (IS_CIRCLE (circle));
}

static void
__circle_copy (IMPointer dest, IMConstPointer src)
{
    Circle *dp = CIRCLE (dest);
    const Circle *sp = CONST_CIRCLE (src);

    assert (dp && sp);

    __circle_parent_class->copy (dest, src);

    dp->radius = sp->radius;
}

static IMBool
__circle_serialize (IMConstPointer circle, IMPointer stream)
{
    const Circle *c  = CONST_CIRCLE (circle);
    IMStream *sp = IM_STREAM (stream);

    assert (c && sp);

    return __circle_parent_class->serialize (circle, stream) &&
           im_stream_put_int32 (stream, c->radius);
}

static IMBool
__circle_deserialize (IMPointer circle, IMPointer stream)
{
    IMInt32  r;

    Circle   *c  = CIRCLE (circle);
    IMStream *sp = IM_STREAM (stream);

    assert (c && sp);

    if (!__circle_parent_class->deserialize (circle, stream))
        return FALSE;

    if (im_stream_get_data_type (stream) != IM_TYPE_INT32 || !im_stream_get_int32 (stream, &r))
        return FALSE;

    c->radius = r;

    return TRUE;
}
static void
__circle_draw (Point *circle)
{
    assert (IS_CIRCLE(circle));

    printf ("-Circle object %p: x = %d, y = %d, R = %d\n",
             (void*)circle,
             circle->x,
             circle->y,
             ((Circle*)circle)->radius);
}

static void
__circle_resize (Circle *circle, int radius)
{
    assert (IS_CIRCLE (circle));

    circle->radius = radius;
}

static void
__circle_class_init (IMPointer klass)
{
    ((IMObjectClass *)klass)->copy  = __circle_copy;
    ((IMObjectClass *)klass)->serialize = __circle_serialize;
    ((IMObjectClass *)klass)->deserialize = __circle_deserialize;

    ((PointClass *)klass)->draw     = __circle_draw;

    ((CircleClass *)klass)->resize  = __circle_resize;

    __circle_parent_class           = im_object_class_get_parent (klass);
}

IMType
circle_get_type ()
{
    static IMTypeInfo circle_info =
    {
        sizeof (CircleClass),
        __circle_class_init,
        0,

        sizeof (Circle),
        __circle_ctor,
        __circle_dtor
    };

    if (!__circle_type) {
        __circle_type = im_type_register_class (TYPE_POINT,
                                               "Circle",
                                               &circle_info,
                                               TRUE);
    }

    return __circle_type;
}

Circle*
circle_new (int x, int y, int radius)
{
    Circle *c = (Circle *) im_object_new (TYPE_CIRCLE);

    assert (c != 0);

    point_move_to (POINT(c), x, y);
 
    c->radius = radius;

    return c;
}

void
circle_resize (Circle *circle, int radius)
{
    const CircleClass *cp = CIRCLE_GET_CLASS (circle);

    assert (cp != 0);

    if (cp->resize)
        cp->resize (circle, radius);
}

int
circle_get_radius (Circle *circle)
{
    assert (IS_CIRCLE (circle));

    return circle->radius;
}

#ifndef NO_TEST_OBJECT_MAIN
int main (int argc, char *argv [])
{
    Point  *p1, *p2;
    Circle *c1;

    im_init (&argc, &argv);

    c1 = circle_new (10, 10, 20);
    p1 = point_new (20, 20);

    printf ("Sizeof c1 = %d\n", im_object_sizeof (c1));
    printf ("Sizeof p1 = %d\n", im_object_sizeof (p1));

    printf ("Typeof c1 = %d\n", im_object_get_type (c1));
    printf ("Typeof p1 = %d\n", im_object_get_type (p1));

    point_draw (p1);
    point_draw (POINT(c1));

    point_move_to (p1, 15, 15);
    point_move_to (POINT(c1), 30, 30);
    circle_resize (c1, 30);

    point_draw (p1);
    point_draw (POINT(c1));

    p2 = POINT (im_object_clone (p1));
    point_draw (p2);
    im_object_unref (p2);

    p2 = POINT (im_object_clone (c1));
    point_draw (p2);

    im_object_unref (p2);

    im_object_ref (p1);
    im_object_ref (p1);
    im_object_ref (c1);
    im_object_ref (c1);
    im_object_unref (p1);
    im_object_unref (p1);
    im_object_unref (c1);
    im_object_unref (c1);

    im_type_print_all_type_informations ();

    im_type_deregister_class (TYPE_CIRCLE);

    im_type_print_all_type_informations ();

    im_type_deregister_class (TYPE_POINT);

    im_type_print_all_type_informations ();

    __circle_type = 0;
    __point_type = 0;

    (void) TYPE_CIRCLE;

    im_type_print_all_type_informations ();

    return 0;
}
#endif
/*
vi:ts=4:nowrap:ai:expandtab
*/
